package com.heima.media.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.dto.PageResponseResult;
import com.heima.common.dto.ResponseResult;
import com.heima.common.dto.User;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.util.MediaThreadLocalUtil;
import com.heima.media.dto.*;
import com.heima.media.entity.WmNews;
import com.heima.media.entity.WmNewsMaterial;
import com.heima.media.entity.WmUser;
import com.heima.media.mapper.WmNewsMapper;
import com.heima.media.service.IWmNewsMaterialService;
import com.heima.media.service.IWmNewsService;
import com.heima.media.service.IWmUserService;
import com.heima.media.service.INewsScanService;
import com.heima.media.vo.NewsVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.sql.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 自媒体图文内容信息表 服务实现类
 * </p>
 *
 * @author SpaceTraveller
 * @since 2022-11-09
 */
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements IWmNewsService {

    @Autowired
    private IWmNewsMaterialService newsMaterialService;

    @Autowired
    @Lazy // 懒加载,避免循环注入出错
    private INewsScanService newsScanService;

    @Autowired
    private IWmUserService userService;

    @Override
    public ResponseResult getNewsList(NewsPageDto dto) {
        User user = MediaThreadLocalUtil.get();
        if (user == null || user.getUserId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 构造查询对象
        LambdaQueryWrapper<WmNews> query = new LambdaQueryWrapper<>();
        // 必须是自己发布的
        query.eq(WmNews::getUserId, user.getUserId());
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
            query.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
        }
        if (dto.getChannelId() != null) {
            query.eq(WmNews::getChannelId, dto.getChannelId());
        }
        if (dto.getStatus() != null) {
            query.eq(WmNews::getStatus, dto.getStatus());
        }
        if (dto.getKeyword() != null) {
            query.like(WmNews::getTitle, dto.getKeyword());
        }
        IPage<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        IPage<WmNews> iPage = this.page(page, query);
        PageResponseResult<List<WmNews>> result = new PageResponseResult<List<WmNews>>();
        result.setCode(AppHttpCodeEnum.SUCCESS.getCode());
        result.setCurrentPage(dto.getPage());
        result.setSize(dto.getSize());
        result.setTotal(iPage.getTotal());
        result.setData(iPage.getRecords());
        return result;
    }

    @Override
    public ResponseResult getNewsById(Long id) {
        WmNews news = this.getById(id);
        return ResponseResult.okResult(news);
    }

    @Override
    @Transactional // 多表操作,必须事务,没有跨微服务,不需要全局事务
    public ResponseResult saveAndUpdateNews(NewsDto dto) throws Exception {
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto, wmNews);
        // 先把要的数据拿到并补全
        User user = MediaThreadLocalUtil.get();
        if (user == null || user.getUserId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        wmNews.setUserId(user.getUserId());
        Date date = new Date(System.currentTimeMillis());
        wmNews.setCreatedTime(date);
        wmNews.setSubmitedTime(date);
        wmNews.setEnable(true);
        // 保存封面信息到wmNews,保存的是JSON字符串
        wmNews.setImages(JSON.toJSONString(dto.getImages()));
        // 如果wmNews的id不为空,那么是修改,这时先要清理掉所有关联关系
        if (wmNews.getId() != null) {
            LambdaQueryWrapper<WmNewsMaterial> query = new LambdaQueryWrapper<WmNewsMaterial>().eq(WmNewsMaterial::getNewsId, wmNews.getId());
            newsMaterialService.remove(query);
        }
        // 拿到内容字符串
        String content = dto.getContent();
        // 提取文章内容中的图片,转换成ContentDto对象处理
        List<ContentDto> contentDtos = JSON.parseArray(content, ContentDto.class);
        // 这里要做一个判断,如果前端传的dto里面的type, 0 是无图  1 是单图  3 是多图  -1 是自动
        // 先处理简单逻辑,用户已经设置了无图,单图,或者多图,
        if (dto.getType() == 0) {
            // 保存封面信息到wmNews
            this.saveOrUpdate(wmNews);// mybatis plus有这个高级货,新增或者更新
            // 都没图,不用管关联资源了
        } else if (dto.getType() == 3 || dto.getType() == 1) { // 那就是1或者3,处理方式相同
            this.saveOrUpdate(wmNews);// mybatis plus有这个高级货,新增或者更新
            // 因为封面图和内容图有排序,所以要分别处理
            // 提取封面图片,并保存到wm_news_material,抽成方法
            saveImageMaterial(wmNews, dto.getImages(), 1);    // 引用类型  0 内容引用  1 主图引用
            // 提取内容中的图片并保存到资源关联库
            List<ImageDto> contentImages = mapToImageDto(contentDtos);
            saveImageMaterial(wmNews, contentImages, 0);
        }
        if (dto.getType() == -1) { //-1是自动,那就拿取数据来看看怎么处理,这就比较复杂了
            List<ImageDto> contentImages = mapToImageDto(contentDtos);
            // 现在看看内容中几张图,1张就单图,1张以上就多图,0就无图,
            int imageSize = contentImages.size();
            if (imageSize == 0) {
                wmNews.setType(0);  // 没有封面图
                // 都没图,不用管关联资源了
                this.saveOrUpdate(wmNews);// mybatis plus有这个高级货,新增或者更新
            }
            if (imageSize == 1) {
                wmNews.setType(1);  // 单图
            }
            if (imageSize > 1) {
                wmNews.setType(3);  // 多图
            }
            if (imageSize > 0) {
                int size = Math.min(imageSize, 3);
                // 保存封面信息到wmNews
                wmNews.setImages(JSON.toJSONString(contentImages.stream().limit(size).collect(Collectors.toList())));
                this.saveOrUpdate(wmNews);// mybatis plus有这个高级货,新增或者更新,这时确保有id了
                List<WmNewsMaterial> contentMaterials = getWmNewsMaterials(wmNews, contentImages, 0);
                List<WmNewsMaterial> coverMaterials = getWmNewsMaterials(wmNews, contentImages, 1);
                // 那就取不超过3张图作为封面
                coverMaterials = coverMaterials.subList(0, size);
                // 保存封面关联图片资源
                // 判断是否有id,没有id是新发布,有id是修改,须要先保存news,因为如果没有id,后面的图片需要
                // if(dto.getId()==null){  //表明是发布新增
                //     this.save(wmNews);
                // }else {
                //     this.updateById(wmNews);
                // }
                newsMaterialService.saveBatch(coverMaterials);
                // 保存内容关联图片资源
                newsMaterialService.saveBatch(contentMaterials);
            }
        }
        if (dto.getStatus() == 1) {// 状态 提交为1  草稿为0,草稿肯定不用提交审核
            // 前面不管怎么走的,最终到这里,调用审核服务,由其提交给阿里云审核,再做后续处理,
            // 这里不应该等待审核结果,所以应该是异步调用
            newsScanService.scanNow(wmNews);
        }
        //判断发布时间,如果已
        return ResponseResult.okResult();
    }

    /**
     * @param id
     * @param enable
     * @return
     */
    @Override
    public ResponseResult upOrDownNews(Integer id, Integer enable) {
        WmNews news = this.getById(id);
        news.setStatus(enable);
        this.updateById(news);
        // 还要调用article上下架
        return ResponseResult.okResult();
    }

    @Override
    public PageResponseResult findPageByName(CheckDto dto) {
        LambdaQueryWrapper<WmNews> query = new LambdaQueryWrapper<>();
        // 草稿就别看了吧?只看需要人工审核的
        query.gt(WmNews::getStatus, 1);
        if (!StringUtils.isEmpty(dto.getTitle())) {
            query.like(WmNews::getTitle, dto.getTitle());
        }
        Page<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        Page<WmNews> iPage = this.page(page, query);
        List<NewsVo> newsVos = iPage.getRecords().stream().map(wmNews -> {
            NewsVo newsVo = new NewsVo();
            BeanUtils.copyProperties(wmNews, newsVo);
            return newsVo;
        }).collect(Collectors.toList());
        // 要补全作者名
        // 拿到本页所有作者id
        List<Integer> usersIds = newsVos.stream().map(NewsVo::getUserId).collect(Collectors.toList());
        LambdaQueryWrapper<WmUser> userQuery = new LambdaQueryWrapper<>();
        userQuery.in(WmUser::getId, usersIds);
        List<WmUser> list = userService.list(userQuery);
        for (NewsVo newsVo : newsVos) {
            for (WmUser user : list) {
                if (user.getId().equals(newsVo.getUserId())) {
                    newsVo.setAuthorName(user.getNickname());
                }
            }
        }
        PageResponseResult result = new PageResponseResult<>(dto.getPage(), dto.getSize(), iPage.getTotal(), newsVos);
        result.setCode(AppHttpCodeEnum.SUCCESS.getCode());
        return result;
    }

    @Override
    public ResponseResult getOneNewsById(Long id) {
        WmNews news = this.getById(id);
        NewsVo newsVo = new NewsVo();
        BeanUtils.copyProperties(news,newsVo);
        WmUser author = userService.getById(news.getUserId());
        newsVo.setAuthorName(author.getNickname());
        return ResponseResult.okResult(newsVo);
    }

    /**
     * @param contentDtos
     * @return
     */
    private List<ImageDto> mapToImageDto(List<ContentDto> contentDtos) {
        return contentDtos.stream().filter(contentDto -> contentDto.getType().equals("image")).map(contentDto -> {
            ImageDto imageDto = new ImageDto();
            imageDto.setId(contentDto.getId());
            imageDto.setUrl(contentDto.getValue());
            return imageDto;
        }).collect(Collectors.toList());
    }

    /**
     * @param wmNews
     * @param imageDtoList
     * @param type         引用类型  0 内容引用  1 主图引用
     */
    private void saveImageMaterial(WmNews wmNews, List<ImageDto> imageDtoList, Integer type) {
        // 引用类型  0 内容引用  1 主图引用
        List<WmNewsMaterial> contentMaterials = getWmNewsMaterials(wmNews, imageDtoList, type);
        if (contentMaterials != null && contentMaterials.size() > 0) {
            // 保存内容图片关联数据
            newsMaterialService.saveBatch(contentMaterials);
        }
    }

    /**
     * @param wmNews
     * @param coverImages
     * @param type        引用类型  0 内容引用  1 主图引用
     * @return
     */
    private List<WmNewsMaterial> getWmNewsMaterials(WmNews wmNews, List<ImageDto> coverImages, Integer type) {
        List<WmNewsMaterial> coverMaterial = coverImages.stream().map(image -> {
            WmNewsMaterial material = new WmNewsMaterial();
            material.setMaterialId(image.getId());
            material.setNewsId(wmNews.getId());
            return material;
        }).collect(Collectors.toList());
        for (int i = 0; i < coverMaterial.size(); i++) {
            WmNewsMaterial material = coverMaterial.get(i);
            material.setType(type);
            material.setOrd(i);
        }
        return coverMaterial;
    }
}
